/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.ssm.changeunit.command;

import com.ssm.changeunit.*;
import com.ssm.changeunit.diff.ContentMerger;
import com.ssm.changeunit.persistance.*;
import com.ssm.changeunit.util.ChangeUnitTraveler;
import com.ssm.changeunit.util.FileUtil;
import java.io.IOException;
import java.util.List;
import org.apache.log4j.Logger;

/**
 *
 * @author 시리우스
 */
public class MergeCommandWorker extends CommandWorker {
    private Logger log = Logger.getLogger(MergeCommandWorker.class);
    
    @Override
    public void work(CommandWorkerOptions commandWorkerOptions) throws CommandWorkerException {
        if( commandWorkerOptions == null )
            throw new CommandWorkerException("Option이 null입니다.");
        
        if( !commandWorkerOptions.hasCommandWorkerOption("dest") )
            throw new CommandWorkerException("Dest Option이 존재하지 않습니다.");
        
        // Branch option을 꺼내옵니다.
        CommandWorkerOption destCommandWorkerOption = commandWorkerOptions.getCommandWorkerOption("dest");
        
        // Branch option에 해당하는 branch들을 가져옵니다.
        String dest = destCommandWorkerOption.getValues()[0];
        
        // item의 값이 없는 경우
        if( dest == null )
            throw new CommandWorkerException("Dest의 값이 존재하지 않습니다.");
        
        // 데이터베이스 체크
        log.debug("데이터베이스가 존재하는지 검사합니다.");
        
        try {
            if(!DatabaseManager.getInstance().isExists())
                throw new CommandWorkerException("데이터베이스가 존재하지 않습니다.");
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
        
        try {
            // Branch를 가져옵니다.
            BranchDAO branchDAO = new BranchDAO();
            
            BranchDTO destBranchDTO = branchDAO.find(dest);
            
            if( destBranchDTO == null )
                throw new CommandWorkerException("Branch가 존재하지 않습니다. Branch: " + dest);
            
            // 타겟 Branch를 Origin Branch에 추가한다.
            ChangeUnitTraveler changeUnitTraveler = new ChangeUnitTraveler(ChangeUnitTraveler.Direct.Parent, destBranchDTO.getChangeUnitId(), destBranchDTO.getOriginChangeUnitId());
            changeUnitTraveler.init();
            
            log.debug(destBranchDTO.getChangeUnitId() + " ~ " + destBranchDTO.getOriginChangeUnitId());
            
            while(changeUnitTraveler.hasNext()) {
                ChangeUnit nextChangeUnit = changeUnitTraveler.getNextChangeUnit();
                
                if( nextChangeUnit.getChangeUnitId().equals(destBranchDTO.getOriginChangeUnitId()) )
                    break;
                
                log.debug("nextChangeUnit: " + nextChangeUnit.getChangeUnitId());
                
                Branch branch = new BranchFactory().createProduct(Environment.getCurrentBranch());
                
                List<Branch> branches = nextChangeUnit.getBranchs();
                
                boolean alreadyHasBranch = false;
                
                for(Branch tmpBranch : branches) {
                    if(tmpBranch.getBranchName().equals(branch.getBranchName())) {
                        alreadyHasBranch = true;
                        break;
                    }
                }
                
                if(!alreadyHasBranch)                
                    nextChangeUnit.addBranch(branch);
            }
            
            // Origin과 dest의 ChangeUnit을 가져옵니다.
            Branch currentBranch = new BranchFactory().createProduct(Environment.getCurrentBranch());
            Branch destBranch = new BranchFactory().createProduct(dest);
            
            ChangeUnit originChangeUnit = currentBranch.getCurrentChangeUnit();
            ChangeUnit destChangeUnit = destBranch.getCurrentChangeUnit();
            
            // Temp ChangeUnit을 생성한다.
            ChangeUnit tempChangeUnit = new ChangeUnitFactory().createProduct("0000-0000-0000");
            
            tempChangeUnit.addBranch(currentBranch);
            
            tempChangeUnit.addParent(originChangeUnit);
            tempChangeUnit.addParent(destChangeUnit);
            
            // CurrentBranch가 Temp ChangeUnit을 가리키도록 한다.
            currentBranch.setCurrentChangeUnit(tempChangeUnit);
            
            /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            // Local의 파일 정보를 가져옵니다.
            List<ManagedItemListDTO> managedItemLists = new ManagedItemListDAO().list();
            List<ChangeUnitItem> destChangeUnitItems = destChangeUnit.getChangeUnitItems();

            log.debug("Local에서 관리되는 파일 리스트");

            for(ManagedItemListDTO managedItemListDTO : managedItemLists) {
                String managedFilePath = managedItemListDTO.getFilePath();

                log.debug("-> " + managedFilePath);

                log.debug("Dest에서 " + managedFilePath +"를 찾습니다.");

                for(ChangeUnitItem changeUnitItem : destChangeUnitItems) {
                    String changeUnitItemFilePath = changeUnitItem.getFilePath();

                    if(managedFilePath.equals(changeUnitItemFilePath)) {
                        log.debug("파일을 찾았습니다.");

                        // 여기서 비교를한 후에..
                        // 파일에 다시 기록해야한다..

                        log.debug("내용을 비교합니다..");

                        ContentMerger contentMerge = new ContentMerger();

                        String originContent = FileUtil.readFromFile(Environment.ROOT_DIR + changeUnitItemFilePath);
                        String targetContent = new String(changeUnitItem.getRefChangeLog().getContent());

                        String[] newContentArray = contentMerge.merge(originContent.split("\n"), targetContent.split("\n"));

                        StringBuilder newContentBuilder = new StringBuilder();

                        for(String line : newContentArray) {
                            log.debug("-> " + line);

                            newContentBuilder.append(line + "\r\n");
                        }

                        // 파일에 기록합니다.
                        log.debug("파일에 기록합니다.");
                        FileUtil.saveDataToFile(changeUnitItemFilePath, newContentBuilder.toString());

                        break;
                    }
                }
            }

            log.debug("Local에서 관리되는 파일 리스트 끝.");

            ManagedItemListFactory managedItemListFactory = new ManagedItemListFactory();

            // 서버의 새 파일도 가져와서 기록해야한다.
            for(ChangeUnitItem changeUnitItem : destChangeUnitItems) {
                String changeUnitItemFilePath = changeUnitItem.getFilePath();

                log.debug("-> " + changeUnitItemFilePath);

                log.debug("ManagedItemList에서 " + changeUnitItemFilePath +"를 찾습니다.");

                for(ManagedItemListDTO managedItemListDTO : managedItemLists) {
                    String managedFilePath = managedItemListDTO.getFilePath();

                    if(!managedFilePath.equals(changeUnitItemFilePath)) {
                        log.debug("파일을 찾을 수 없습니다. 새로운 파일입니다.");

                        // 로컬 파일에 기록해야한다.
                        FileUtil.saveDataToFile(changeUnitItemFilePath, new String(changeUnitItem.getRefChangeLog().getContent()));

                        // ManagedItemList에 추가..
                        managedItemListFactory.createProduct(changeUnitItemFilePath);

                        break;
                    }
                }
            }

            ManagedChangeUnitDTO managedChangeUnitDTO = new ManagedChangeUnitDAO().find();
            managedChangeUnitDTO.setManagedChangeUnitId(tempChangeUnit.getChangeUnitId());
            new ManagedChangeUnitDAO().update(managedChangeUnitDTO);
        }
        catch(IOException ex) {
            throw new CommandWorkerException(ex);
        }
        catch (PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
        catch(ChangeUnitException ex) {
            throw new CommandWorkerException(ex);
        }
    }
}
